/**
 * 
 */
package ca.csf.dfc.poo.environnement;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import ca.csf.dfc.poo.exceptions.IllegalPositionXException;
import ca.csf.dfc.poo.exceptions.IllegalPositionYException;
import ca.csf.dfc.poo.exceptions.IllegalRecompenseException;


/**
 * @author Equipe 1
 *
 */
public class Environnement
{
    /**
     * Tableau de cases représentant l'environnement.
     */
	private Case[][] m_tableCase;
	
	/**
	 * Données statique utilisées pour lire le fichier xml
	 */
	private static final String ELM_ENVIRONNEMENT = "environnement";
	private static final String ELM_LIGNE = "ligne";
	private static final String ELM_CASE = "case";
	private static final String ATT_TYPE = "type";

	/**
	 * Constructeur par initialisation
	 * @param p_chemin Chemin d'accès au fichier XML qui contient l'environnement.
	 * @throws IllegalRecompenseException 
	 */
	public Environnement(String p_chemin) throws IllegalRecompenseException
	{
		this.setTableCase(ConstruireTableau(p_chemin));
	}
	
	/**
	 * 
	 * @return La table contenant l'environnement.
	 */
	public Case[][] getTableCase()
	{
		return this.m_tableCase;
	}

	/**
	 * Remplace le tableau de cases courant par un autre.
	 * @param p_tableCase Table d'environnement à assigner à m_tableCase
	 */
	private void setTableCase(Case[][] p_tableCase)
	{
		this.m_tableCase = p_tableCase;
	}

	/**
	 * Méthode qui permet de construire un tableau d'environnement à partir d'un fichier XML
	 * @param p_chemin chemin d'accès au fichier XML.
	 * @return Tableau contenant l'environnement.
	 * @throws IllegalRecompenseException 
	 */
	private Case[][] ConstruireTableau(String p_chemin) throws IllegalRecompenseException
	{
		ArrayList<ArrayList<Case>> maListe = new ArrayList<ArrayList<Case>>();
		int compteur = 0;
		XMLStreamReader doc = null;
		
		try
		{
			FileReader input = new FileReader(new File(p_chemin));
			
			doc = XMLInputFactory.newInstance().createXMLStreamReader(input);
			
			doc.nextTag();
			//On vérifie que le fichier xml contient un élément environnement, si non, une erreur est générée.
			if(!doc.getLocalName().equals(ELM_ENVIRONNEMENT)) 
			{
				throw new XMLStreamException("Aucun environnement trouvé.");
			}
			
			doc.nextTag();


			while(doc.isStartElement())
			{
				
				if(doc.getLocalName().equals(ELM_LIGNE))//Si l'élément est une ligne.
				{
					doc.nextTag();
					maListe.add(new ArrayList<Case>());
					
					//Tant qu'il y a des cases dans la ligne, on l'ajoute à la liste
					while(doc.isStartElement() && doc.getLocalName().equals(ELM_CASE))
					{
						String typeCase = doc.getAttributeValue("", ATT_TYPE);
						
						maListe.get(compteur).add(DeterminerType(typeCase));
						doc.nextTag();
						doc.nextTag();
					}
					compteur++;
				}
				doc.nextTag();

			}
		}
		catch(XMLStreamException exp)
		{
			System.err.println("Fichier XML corrompu "+ exp);
		}
		catch(Exception exp)
		{
			
		}
		finally //On ferme le fichier.
		{
			if(doc != null)
			{
				try
				{
					doc.close();
				}
				catch(XMLStreamException exp)
				{
					System.err.println("Erreur lors de la fermeture "+ exp);
				}
				finally
				{
					doc = null;
				}
			}
		}
		
		
		//On transforme le ArrayList vue programmeur en tableau fixe plan cartésien.
		Case[][] listeCase = new Case[maListe.get(0).size()][maListe.size()];
		int j = 0;
		for(ArrayList<Case> c: maListe)
		{
			for(int i=0; i<c.size(); i++)
			{
				listeCase[i][j]= c.get(i);
			}
			j++;
		}
		
		return listeCase;
	}
	
	/**
	 * Détermine la case du fichier xml
	 * @param p_type String contenant le type de case
	 * @return la case correspondante à p_type
	 * @throws IllegalRecompenseException 
	 */
	private Case DeterminerType(String p_type) throws IllegalRecompenseException
	{
		Case maCase;
		if(p_type.equals("tapis"))
		{
			maCase = new Tapis();
		}
		else if(p_type.equals("plancher"))
		{
			maCase = new Plancher();
		}
		else if(p_type.equals("prise"))
		{
			maCase = new Prise();
		}
		else
		{
			maCase = new Interdit();
		}
		
		return maCase;
	}
	
	/**
	 * Méthode qui retourne la largeur de l'environnement
	 * @return Entier correspondant à la largeur de l'environnement
	 */
	public int GetLargeur()
	{
		return this.getTableCase().length;
	}
	
	/**
	 * Méthode qui retourne la hauteur de l'environnement
	 * @return Entier correspondant à la hauteur de l'environnement.
	 */
	public int GetHauteur()
	{
		return this.getTableCase()[0].length;
	}
	
	/**
	 * Méthode qui détermine si une case est valide, elle est donc dans l'environnement et non interdite
	 * @param p_x position de la case en x
	 * @param p_y position de la case en y
	 * @return true si la case n'est pas interdite
	 */
	public boolean CaseValide(int p_x, int p_y)
	{
		
		boolean valide = false;
		if((p_x>=0 && p_x< this.GetLargeur())&&(p_y>=0 && p_y<this.GetHauteur()))
		{
			if(this.getTableCase()[p_x][p_y] instanceof Interdit)
			{
				valide = false;
			}
			else
			{
				valide = true;
			}
		}
			
		return valide;		
	}

	/**
	 * Méthode qui renvoie un tableau de 4 cases contenant les voisins directs d'une case donnée
	 * @param p_x position de la case en x
	 * @param p_y position de la case en y
	 * @return un tableau de 4 cases contenant les voisins directs de la case à la position x, y
	 * @throws IllegalPositionXException 
	 * @throws IllegalPositionYException 
	 */
	public Case[] GetVoisin(int p_x, int p_y) throws IllegalPositionXException, IllegalPositionYException
	{
		//On vérifie que les cases sont valides.
	    if ((p_x < 0) || (p_x >= this.GetLargeur()))
	        throw new IllegalPositionXException("La valeur ne doit pas dépasser "+this.GetLargeur());
	    
        if ((p_y < 0) || (p_y >= this.GetHauteur()))
            throw new IllegalPositionYException("La valeur ne doit pas dépasser "+this.GetHauteur());
        
		Case[] tableauRetour = new Case[4];
		
		
		
		if(!(p_y < 1))
		{
			tableauRetour[0] = this.m_tableCase[p_x][p_y-1];
		}
		if(!(p_x >= this.GetLargeur()-1))
		{
			tableauRetour[1] = this.m_tableCase[p_x+1][p_y];
		}
		if(!(p_y >= this.GetHauteur()-1))
		{
			tableauRetour[2] = this.m_tableCase[p_x][p_y+1];
		}
		if(!(p_x < 1))
		{
			tableauRetour[3] = this.m_tableCase[p_x-1][p_y];
		}			
		return tableauRetour;
	}
}
